<template>
  <TresGroup>
    <TresMesh ref="gridPalneRef">
      <TresPlaneGeometry :args="[40, 40]" />
      <TresShaderMaterial :uniforms="plane.uniforms" :vertex-shader="plane.vertexShader"
        :fragment-shader="plane.fragmentShader" :side="THREE.DoubleSide" :transparent="false" />
    </TresMesh>
    <primitive v-for="(item, key) in arrows" :key="key" :object="item" />
    <primitive v-for="(item, key) in axesList" :key="key" :object="item.line">
      <TresSprite :position="item.text.position">
        <TresSpriteMaterial :map="item.text.texture" transparent :alpha-test="0.1" />
      </TresSprite>
    </primitive>
    <Elements />
    <PointerIndicator />
    <PointerEl show-line :data="pointer" :visible="isCreating && enable" />
    <AddIcon :visible="isCreating && !enable" />
  </TresGroup>
</template>
<script lang="ts" setup>
import * as THREE from 'three'
import { useTresContext } from '@tresjs/core'
import { use3DEditorStore } from '@/store'
import { storeToRefs } from 'pinia'
import useGrid from '../hooks/useGrid'
import AddIcon from './AddIcon.vue'
import PointerIndicator from './indicator/index.vue'
import Elements from './element/index.vue'
import PointerEl from './element/Pointer.vue'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'

const editorStore = use3DEditorStore()
const { isCreating } = storeToRefs(editorStore)
const context = useTresContext()

const gridPalneRef = shallowRef<THREE.Mesh>()
const arrows = shallowRef<THREE.ArrowHelper[]>([])
const axesList = shallowRef<axesItem[]>([])
const {
  enable,
  pointer,
  handlePointerDown,
  handlePointerMove,
  handlePointerup,
} = useGrid(gridPalneRef.value!, context)

const plane = {
  side: THREE.DoubleSide,
  uniforms: {
    color: { value: new THREE.Color(0x000000) },
    opacity: { value: 0.3 },
  },
  vertexShader: `
    varying vec2 vUv;
      void main() {
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        vUv = uv;
      }`,
  fragmentShader: `
    uniform vec3 color;
    uniform float opacity;
    varying vec2 vUv;
    void main() {
      float alpha = smoothstep(0.0,1.0,opacity);
      alpha *= smoothstep(0.0,0.1,vUv.x);
      alpha *= smoothstep(1.0,0.9,vUv.x);
      alpha *= smoothstep(0.0,0.1,vUv.y);
      alpha *= smoothstep(1.0,0.9,vUv.y);

      gl_FragColor = vec4(color,alpha);
    }`,
}

const textureCache = new Map<string, THREE.CanvasTexture>()

function createTexure(text: string) {
  if (textureCache.has(text)) {
    return textureCache.get(text)!
  }
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
  ctx.font = '20px Arial'
  const width = ctx.measureText(text).width + 10
  canvas.width = width
  canvas.height = 30
  ctx.fillStyle = 'rgba(0,0,0,0)' // 设置透明背景
  ctx.fillRect(0, 0, width, 30)
  ctx.fillStyle = `rgb(0,0,0)`
  ctx.fillText(text, 5, 20)

  const texture = new THREE.CanvasTexture(canvas)
  textureCache.set(text, texture)
  return texture
}

class axesItem {
  line!: THREE.Line
  text!: {
    label: string;
    position: VerticesType;
    texture: THREE.CanvasTexture;
  }
}

const axis = {
  axesData: [],
  tickLength: 0.2,
  axisLength: 20,
  fontPath: '/fonts/optimer_bold.typeface.json',
  create: () => {
    const axesArray = ['x', 'y', 'z']
    const axisLength = 20
    const step = 5
    const tickLength = 0.2

    // 坐标轴
    axesArray.forEach((dir) => {
      const colorStr =
        dir === 'x' ? '1, 0, 0' : dir === 'y' ? '0, 1, 0' : '0, 0, 1'
      const color = new THREE.Color(...colorStr.split(','))

      const dirVector = new THREE.Vector3()
      dirVector[dir === 'x' ? 'x' : dir === 'y' ? 'y' : 'z'] = axisLength

      const arrow = new THREE.ArrowHelper(
        dirVector,
        dirVector.clone().multiplyScalar(-1),
        axisLength * 2,
        color,
        0.5,
        0.5,
      )
      arrows.value.push(arrow!)
    })

    // 刻度和标签
    const tickGeometry = new THREE.BufferGeometry()

    for (const dir of axesArray) {
      for (let i = -axisLength; i <= axisLength; i += step) {
        if (i !== 0) {
          const item = new axesItem()
          const pos = new THREE.Vector3(
            dir === 'x' ? i : 0,
            dir === 'y' ? i : 0,
            dir === 'z' ? i : 0,
          )
          const dirVec = new THREE.Vector3(
            dir === 'x' ? 1 : 0,
            dir === 'y' ? 1 : 0,
            dir === 'z' ? 1 : 0,
          )
          const tickGeom = tickGeometry.clone()
          tickGeom.translate(pos.x, pos.y, pos.z)
          tickGeom.lookAt(dirVec)
          tickGeom.translate(0, 0, -tickLength / 2)

          tickGeom.setFromPoints([
            pos,
            pos.clone().add(dirVec.multiplyScalar(tickLength)),
          ])
          const line = new THREE.Line(
            tickGeom,
            new THREE.LineBasicMaterial({ color: 0x000000 }),
          )
          item.line = line

          if (Math.abs(i) % step === 0) {
            const label = i.toString()
            const texture = createTexure(label)
            const position = pos.toArray()
            if (dir === 'x') {
              position[1] -= 0.8
            }
            else if (dir === 'y') {
              position[2] -= 0.8
            }
            else if (dir === 'z') {
              position[0] += 0.8
            }
            item.text = { label, position, texture }
          }
          axesList.value.push(item)
        }
      }
    }
  },
}

// 异步加载字体文件
function loadFont() {
  const loader = new FontLoader()
  return new Promise((resolve, reject) => {
    loader.load(axis.fontPath, resolve, undefined, reject)
  })
}
loadFont().then((font) => {
  console.log('Font loaded:', font)
}).catch((err) => {
  console.error('Failed to load font:', err)
})

axis.create()
</script>
